package be.dch.guice.extra;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class TrueSingletonProvider<T> implements Provider<T> {

    @Inject
    private Injector injector;
    private static Map<Key<?>, Object> singletonCache;
    private static final Lock lock = new ReentrantLock();

    private static final Logger LOG = Logger.getLogger(TrueSingletonProvider.class);
    private final Key<T> boundKey;

    public TrueSingletonProvider(final Class iface) {
	TrueSingletonProvider.LOG.debug("Constructed (" + iface + ")");
	this.boundKey = Key.get(iface, SingletonInternal.class);
    }

    private boolean instanceReady() {
	return TrueSingletonProvider.singletonCache.containsKey(this.boundKey);
    }

    public T get() {
	TrueSingletonProvider.LOG.debug("get singleton (" + this.boundKey + ")");
	if (!this.instanceReady()) {
	    try {
		TrueSingletonProvider.lock.lock();
		if (!this.instanceReady()) {
		    TrueSingletonProvider.LOG.debug("instantiate singleton (" + this.boundKey + ")");

		    TrueSingletonProvider.singletonCache.put(this.boundKey, this.injector.getInstance(this.boundKey));
		}
	    } finally {
		TrueSingletonProvider.lock.unlock();
	    }
	}
	return (T) TrueSingletonProvider.singletonCache.get(this.boundKey);
    }

    static {
	TrueSingletonProvider.LOG.debug("static {} - Booting....");
	TrueSingletonProvider.singletonCache = new ConcurrentHashMap();
    }
}